#ifndef RT_USE_OLD_CONN_WRAP

#include "RtNetworkBase.h"
#include "RtConnRlbTcp.h"
#include "RtConnRlbTcpPdu.h"
#include "RtThread.h"

#define RT_CONN_KEEPALIVE_INTERVAL 30 /// in seconds
#define RT_CONN_RECONNECT_TIMEOUT 30 /// in seconds
#define RT_CONN_IDLE_TIMEOUT 300 /// in seconds

#define CT_PROTO_LEN 16 /// in bits

#define RT_CONN_TYPE_STRING(connType) \
	( (connType==CRtConnectionManager::CTYPE_PACKAGE_TCP) ? "[Package-TCP]" : \
	 ( (connType==CRtConnectionManager::CTYPE_RELIABLE_TCP) ? "[Reliable-TCP]" : "Unknown") )

//////////////////////////////////////////////////////////////////////////
// class CRtConnRlbTcp
//////////////////////////////////////////////////////////////////////////

CRtConnRlbTcp::CRtConnRlbTcp(CRtConnectionManager::CType inConnType)
: m_connType(inConnType)
, m_channelId(0)
, m_ackFromPeer(0)
, m_ticket(0)
, m_sendBuffer(this)
, m_recvBuffer(NULL)
, m_ackToPeer(0)
, m_unackedDataLen(0)
, m_keepAliveInterval(RT_CONN_KEEPALIVE_INTERVAL)
, m_continuousIdleTime(0)
, m_isDataReceived(FALSE)
, m_isDataSent(FALSE)
, m_needOnSend(FALSE)
{
	Reset4Recv();
}

CRtConnRlbTcp::~CRtConnRlbTcp()
{
	if (m_status==CS_UNCONNECTED)
		return;

	Close_i();
}

RtResult CRtConnRlbTcp::SendData(
		CRtMessageBlock& inData,
		BOOL inSendImmediately,
		BOOL inDestroyData,
		DataPriority inPriority, 
		BOOL inNeedAck)
{
	if (m_status==CS_UNCONNECTED)
		return RT_ERROR_NOT_AVAILABLE;

	if ( m_needOnSend || !m_sendBuffer.HaveSpace() )
	{
		m_needOnSend = TRUE;
		return RT_ERROR_PARTIAL_DATA;
	}

	DWORD dataLength = inData.GetChainedLength();
	if (dataLength==0)
		return RT_OK;

	m_isDataSent = TRUE;
#ifdef RLBTCP_ENABLE_DATA_PRIORITY
	m_sendBuffer.AddData(inDestroyData ? &inData : inData.DuplicateChained(), dataLength, inPriority, inNeedAck);
#else
	m_sendBuffer.AddData(inDestroyData ? &inData : inData.DuplicateChained(), dataLength, IRtTransport::DP_MEDIUM, inNeedAck);
#endif

	if (m_status==CS_HANDSHAKE_OK && m_sendBuffer.m_encodedData==NULL)
	{
		/// Also send immediately if sending buffer have no space now.
		if (inSendImmediately || !m_sendBuffer.HaveSpace())
		{
			m_sendBuffer.SendBuffer();
			return RT_OK;
		}

		if (!m_sendDelayTimer.IsScheduled())
		{
			/// Send delay xxx milliseconds...
			m_sendDelayTimer.Schedule(this, /*CRtTimeValue(1, 0)*/ CRtTimeValue(0, 200000), 1);
			return RT_OK;
		}
	}

	return RT_OK;
}

RtResult CRtConnRlbTcp::SetOption(DWORD inCommand, LPVOID inArg)
{
	switch(inCommand)
	{
	case CS_OPT_MAX_SENDBUF_LEN:
		m_sendBuffer.SetMaxSize( *(static_cast<DWORD*>(inArg)) );
		RT_INFO_TRACE_THIS("CRtConnRlbTcp::SetOption, new buffer max size="<<m_sendBuffer.m_maxSize);
		return RT_OK;

	case CS_OPT_KEEPALIVE_INTERVAL:
		m_keepAliveInterval = *(static_cast<WORD*>(inArg));
		RT_INFO_TRACE_THIS("CRtConnRlbTcp::SetOption, new keepalive interval="<<m_keepAliveInterval);
		if (m_status==CS_HANDSHAKE_OK && RT_BIT_ENABLED(m_connType, CRtConnectionManager::CTYPE_PDU_KEEPALIVE))
			m_timer.Schedule(this, (time_t)m_keepAliveInterval);
		return RT_OK;

	default:
		if (m_lowerTransport.Get())
			return m_lowerTransport->SetOption(inCommand, inArg);
	}

	return RT_ERROR_NOT_AVAILABLE;
}

RtResult CRtConnRlbTcp::GetOption(DWORD inCommand, LPVOID outArg) 
{
	switch(inCommand)
	{
	case RT_OPT_TRANSPORT_TRAN_TYPE:
		*(static_cast<CRtConnectionManager::CType*>(outArg)) = m_connType;
		return RT_OK;
		
	case CS_OPT_MAX_SENDBUF_LEN:
		*(static_cast<DWORD*>(outArg)) = m_sendBuffer.m_maxSize;
		return RT_OK;

	case CS_OPT_KEEPALIVE_INTERVAL:
		*(static_cast<WORD*>(outArg)) = m_keepAliveInterval;
		return RT_OK;

	default:
		if (m_lowerTransport.Get())
			return m_lowerTransport->GetOption(inCommand, outArg);
	}

	return RT_ERROR_NOT_AVAILABLE;
}

void CRtConnRlbTcp::OnReceive(
		CRtMessageBlock &inData,
		IRtTransport *aTrptId,
		CRtTransportParameter *aPara)
{
	m_isDataReceived = TRUE;

	if (m_recvBuffer)
		m_recvBuffer->Append(inData.DuplicateChained());
	else
		m_recvBuffer = inData.DuplicateChained();

	DWORD recvBufferLen = m_recvBuffer->GetChainedLength();
	while ( recvBufferLen >= ( m_recvDataLen==0 ? (m_recvPduType==0 ? m_recvPduLen : m_recvPduLen-1) : m_recvDataLen ) )
	{
		if (m_recvPduType==0)
		{
			m_recvBuffer->Read(&m_recvPduType, 1);
			if ( ( m_recvPduLen = CRtConnRlbTcpPduBase::GetFixLength(m_recvPduType) ) == 0 )
			{
				RT_ERROR_TRACE_THIS("CRtConnRlbTcp::OnReceive, invalid pdu type="<<m_recvPduType);
				OnException(RT_ERROR_NETWORK_INVALID_PDU);
				return;
			}
// 
// 			--recvBufferLen;
		}

		if (m_recvDataLen==0 && recvBufferLen<m_recvPduLen)
			return;

		switch(m_recvPduType)
		{
		case RT_CONN_RLBTCP_PDU_DATA:
		case RT_CONN_RLBTCP_PDU_DATA_WITH_ACK:
		case RT_CONN_RLBTCP_PDU_DATA_NEED_ACK:
			{
				if (m_recvDataLen==0)
				{
					CRtConnRlbTcpPduData dataPdu(m_recvPduType);
					dataPdu.DecodeFixLength(*m_recvBuffer);
					recvBufferLen -= m_recvPduLen;
					m_ackFromPeer = dataPdu.GetAck();
#ifdef RLBTCP_ENABLE_SHORT_DATALEN
					m_recvDataLen = dataPdu.GetShortLength();
					m_gotDataLen = (m_recvDataLen==0xFF) ? FALSE : TRUE;
#else
					m_recvDataLen = dataPdu.GetLength();
#endif
					if (m_recvPduType!=RT_CONN_RLBTCP_PDU_DATA)
						OnRecvAck(m_ackFromPeer);
				}

#ifdef RLBTCP_ENABLE_SHORT_DATALEN
				if (!m_gotDataLen)
				{
					if (recvBufferLen<sizeof(DWORD))
					{
						m_recvDataLen = sizeof(DWORD);
						return;
					}
					CRtByteStreamIntel is(*m_recvBuffer);
					is>>m_recvDataLen;
					recvBufferLen -= sizeof(DWORD);
					m_gotDataLen = TRUE;
				}
#endif

				if (recvBufferLen < m_recvDataLen)
					return;

				CRtMessageBlock* mbData = m_recvBuffer;
				m_recvBuffer = m_recvBuffer->Disjoint(m_recvDataLen);
				recvBufferLen -= m_recvDataLen;
				if (m_recvPduType==RT_CONN_RLBTCP_PDU_DATA_NEED_ACK)
				{
					++m_ackToPeer;
					m_unackedDataLen += m_recvDataLen;
					if (m_unackedDataLen>4096)
					{
						SendKeepAlive();
					}
				}
				Reset4Recv();

				RT_ASSERTE(m_sink);
				if (m_sink)
					m_sink->OnReceive(*mbData, this);
				mbData->DestroyChained();
			}

			continue;

		case RT_CONN_RLBTCP_PDU_CONN_REQ:
			{
				CRtConnRlbTcpPduConnReq connReqPdu;
				connReqPdu.Decode(*m_recvBuffer);
				recvBufferLen -= m_recvPduLen;
				m_connType = ( ( (DWORD)connReqPdu.GetConnAttr() ) << CT_PROTO_LEN ) | (m_connType & CRtConnectionManager::CTYPE_TYPE_MASK);
				m_channelId = connReqPdu.GetChannelId();
				m_ackFromPeer = connReqPdu.GetAck();
				m_ticket = connReqPdu.GetTicket();
			}

			Reset4Recv(); 
			OnRecvConnReq();
			continue;

		case RT_CONN_RLBTCP_PDU_CONN_RESP:
			recvBufferLen -= m_recvPduLen;
			Reset4Recv();
			OnRecvConnResp();
			continue;
			
		case RT_CONN_RLBTCP_PDU_DISCONN:
			OnRecvDisconn();
//			recvBufferLen -= m_recvPduLen;
			return;

		case RT_CONN_RLBTCP_PDU_KEEPALIVE:
			{
				CRtConnRlbTcpPduKeepalive keepalivePdu;
				keepalivePdu.Decode(*m_recvBuffer);
				if (RT_BIT_ENABLED(m_connType, CRtConnectionManager::CTYPE_PDU_ACK))
					OnRecvAck(keepalivePdu.GetAck());
				recvBufferLen -= m_recvPduLen;
				if (recvBufferLen==0)
				{
					m_recvBuffer->DestroyChained();
					m_recvBuffer = NULL;
				}
				Reset4Recv();
			}
			continue;

		case RT_CONN_RLBTCP_PDU_RTT:
			{
				CRtConnRlbTcpPduRtt rttPdu;
				rttPdu.Decode(*m_recvBuffer);
				recvBufferLen -= m_recvPduLen;
				if (recvBufferLen==0)
				{
					m_recvBuffer->DestroyChained();
					m_recvBuffer = NULL;
				}
				OnRecvRtt(rttPdu.GetTimestamp());
				Reset4Recv();
			}
			continue;

		default:
			RT_ASSERTE(FALSE);
		}
	}
}

void CRtConnRlbTcp::OnSend(
		IRtTransport *aTrptId,
		CRtTransportParameter *aPara)
{
	RtResult rv = m_sendBuffer.SendEncodedData();
	if (RT_FAILED(rv))
		return;

	m_sendBuffer.SendBuffer();
	if (m_needOnSend && m_sendBuffer.HaveSpace())
	{
		m_needOnSend = FALSE;
		if (m_sink)
			m_sink->OnSend(this);
	}
}

void CRtConnRlbTcp::OnRecvAck(SeqType inAck)
{
	if (m_sendBuffer.OnRecvAck(inAck) && m_needOnSend)
	{
		m_needOnSend = FALSE;
		if (m_sink)
			m_sink->OnSend(this);
	}
}

void CRtConnRlbTcp::SendKeepAlive()
{
	if ( m_status!=CS_HANDSHAKE_OK || !m_sendBuffer.IsDataSetEmpty() )
		return;

	m_sendBuffer.SendKeepAlive();
}

void CRtConnRlbTcp::SendRtt(DWORD inTimestamp)
{
	if ( m_status!=CS_HANDSHAKE_OK )
		return;
	
	m_sendBuffer.SendRtt(inTimestamp);
}

void CRtConnRlbTcp::SendDisconn()
{
	if ( m_status!=CS_HANDSHAKE_OK )
		return;

	RtResult rv = m_sendBuffer.SendEncodedData();
	if (RT_FAILED(rv))
		return;

	rv = m_sendBuffer.SendBuffer();
	if (RT_FAILED(rv))
		return;

	CRtConnRlbTcpPduDisconn disconnPdu;
	CRtMessageBlock mb(CRtConnRlbTcpPduBase::GetFixLength(RT_CONN_RLBTCP_PDU_DISCONN));
	disconnPdu.Encode(mb);
	m_lowerTransport->SendData(mb);
}

void CRtConnRlbTcp::Close_i(RtResult inReason)
{
	SetStatus(CS_UNCONNECTED);
	m_timer.Cancel();
	m_sendDelayTimer.Cancel();
	if (m_lowerTransport.Get())
	{
		m_lowerTransport->Disconnect(inReason);
		m_lowerTransport = NULL;
	}
	ReleaseRecvBuffer();
}

void CRtConnRlbTcp::ReleaseRecvBuffer()
{
	if (m_recvBuffer)
	{
		m_recvBuffer->DestroyChained();
		m_recvBuffer = NULL;
	}
}

//////////////////////////////////////////////////////////////////////////
// class CRtConnRlbTcpClient
//////////////////////////////////////////////////////////////////////////

CRtConnRlbTcpClient::CRtConnRlbTcpClient(CRtConnectionManager::CType inConnType)
		: CRtConnRlbTcp(inConnType&CRtConnectionManager::CTYPE_RELIABLE_TCP|CRtConnectionManager::CTYPE_TCP)
		, m_lastRtt(0)
{
}

CRtConnRlbTcpClient::~CRtConnRlbTcpClient()
{
}

RtResult CRtConnRlbTcpClient::Disconnect(RtResult inReason)
{
	if (m_status==CS_UNCONNECTED)
		return RT_OK;

	RT_INFO_TRACE_THIS("CRtConnRlbTcpClient::Disconnect, reason="<<inReason<<" status="<<m_status);
	m_sink = NULL;
	m_reconnectTimer.Cancel();
	SendDisconn();
	Close_i(inReason);
	if (m_connConnector)
	{
		m_connConnector->GetTPConnector()->CancelConnect();
		m_connConnector = NULL;
	}
	return RT_OK;
}

void CRtConnRlbTcpClient::OnDisconnect(RtResult inReason, IRtTransport* inTransport)
{
	RT_INFO_TRACE_THIS("CRtConnRlbTcpClient::OnDisconnect, reason="<<inReason<<" trpt="<<inTransport<<" status="<<m_status);

	m_lowerTransport = NULL;
	ReleaseRecvBuffer();
	m_sendBuffer.ReleaseEncodedData();

	if (m_status==CS_CONNECTED)
	{
		SetStatus(CS_UNCONNECTED);
		m_connConnector->GetConnectorSink()->OnConnectIndication(inReason, NULL, m_connConnector.ParaIn());
		m_connConnector = NULL;
		return;
	}
	
	if (m_status==CS_HANDSHAKE_OK)
	{
		m_timer.Cancel();
		m_sendDelayTimer.Cancel();
		if ( RT_BIT_DISABLED(m_connType, CRtConnectionManager::CTYPE_PDU_RECONNECT) )
		{
			SetStatus(CS_UNCONNECTED);
			if (m_sink)
				m_sink->OnDisconnect(inReason, this);
			m_connConnector = NULL;
			return;
		}

		SetWaitReconnectStatus();
		m_timer.Schedule(this, (time_t)RT_CONN_RECONNECT_TIMEOUT, 1);
		return;
	}

	RT_ASSERTE(m_status==CS_RECONNECTED);
	SetWaitReconnectStatus();
}

void CRtConnRlbTcpClient::OnConnectIndication(
		RtResult inResult,
		IRtTransport* inTransport,
		IRtAcceptorConnectorId* inRequestId)
{
	RT_INFO_TRACE_THIS("CRtConnRlbTcpClient::OnConnectIndication, result="<<inResult<<" trpt="<<inTransport);

	if(RT_SUCCEEDED(inResult))
	{
		SetStatus(m_status==CS_INIT ? CS_CONNECTED : CS_RECONNECTED);
		m_lowerTransport = inTransport;
		m_lowerTransport->OpenWithSink(this);

		SendConnReq();
		return;
	}

	if (m_status==CS_INIT)
	{
		SetStatus(CS_UNCONNECTED);
		m_connConnector->GetConnectorSink()->OnConnectIndication(inResult, NULL, m_connConnector.ParaIn());
		m_connConnector = NULL;
		return;
	}

	RT_ASSERTE(m_status==CS_RECONNECTING);
	SetWaitReconnectStatus();
}

void CRtConnRlbTcpClient::CancelHandShake()
{
	if (m_status==CS_CONNECTED)
	{
		SetStatus(CS_UNCONNECTED);
		RT_ASSERTE(m_lowerTransport.Get());
		m_lowerTransport->Disconnect(RT_OK);
		m_lowerTransport = NULL;
	}
}

void CRtConnRlbTcpClient::OnTimer(CRtTimerWrapper* inId)
{
	if (&m_sendDelayTimer==inId)
	{
		if (m_sendBuffer.m_encodedData!=NULL)
			return;

		RT_ASSERTE_RETURN_VOID(m_status==CS_HANDSHAKE_OK);
		m_sendBuffer.SendBuffer();
		return;
	}

	if (inId==&m_reconnectTimer)
	{
		RT_INFO_TRACE_THIS("CRtConnRlbTcpClient::OnTimer, reconnecting ..."
			" ip="<<m_connConnector->GetPeerAddr().GetIpDisplayName()<<
			" port="<<m_connConnector->GetPeerAddr().GetPort());

		RT_ASSERTE(m_status==CS_WAIT_RECONNECT);
		SetStatus(CS_RECONNECTING);

		Reset4Recv();

		CRtTimeValue tv((double)10L);
		m_connConnector->GetTPConnector()->AsycConnect(this, m_connConnector->GetPeerAddr(), &tv);
		return;
	}

	RT_ASSERTE(&m_timer==inId);

	if (m_status!=CS_HANDSHAKE_OK)
	{
		RT_WARNING_TRACE_THIS("CRtConnRlbTcpClient::OnTimer, reconnecting timeout! status="<<m_status);
		m_reconnectTimer.Cancel();
		m_connConnector->GetTPConnector()->CancelConnect();
		goto close_end;
	}

	if (!m_isDataReceived)
	{
		m_continuousIdleTime += m_keepAliveInterval;
		if (m_continuousIdleTime>=RT_CONN_IDLE_TIMEOUT)
		{
			RT_WARNING_TRACE_THIS("CRtConnRlbTcpClient::OnTimer, connection idle timeout! time="<<m_continuousIdleTime);
			goto close_end;
		}
	}
	else
	{
		m_continuousIdleTime = 0;
		m_isDataReceived = FALSE;
	}

	if (!m_isDataSent)
		SendKeepAlive();
	else
		m_isDataSent = FALSE;

	/// Send RTT detecting PDU. 
	SendRtt((DWORD)GetTickCountEx());

	return;

close_end:

	Close_i(RT_ERROR_NETWORK_CONNECT_TIMEOUT);
	if (m_sink)
		m_sink->OnDisconnect(RT_ERROR_NETWORK_CONNECT_TIMEOUT, this);
	m_connConnector = NULL;
}

void CRtConnRlbTcpClient::OnRecvConnResp()
{
	/// Cancel the reconnect-timeout timer. 
	m_timer.Cancel();

	CRtConnRlbTcpPduConnResp respPdu;
	respPdu.Decode(*m_recvBuffer);

	WORD lastStatus = m_status;
	if (RT_FAILED(respPdu.GetResult() ) )
	{
		RT_WARNING_TRACE_THIS("CRtConnRlbTcpClient::OnRecvConnResp, handshake failed! rv="<<respPdu.GetResult()<<" status="<<m_status);
		Close_i();
		if (lastStatus==CS_CONNECTED)
			m_connConnector->GetConnectorSink()->OnConnectIndication(respPdu.GetResult(), NULL, m_connConnector.ParaIn());
		else if (m_sink)
			m_sink->OnDisconnect(respPdu.GetResult(), this);
		m_connConnector = NULL;
		return;
	}

	SetStatus(CS_HANDSHAKE_OK);
	m_channelId = respPdu.GetChannelId();
	m_ackFromPeer = respPdu.GetAck();
	m_ticket = respPdu.GetTicket();
	if (RT_BIT_ENABLED(m_connType, CRtConnectionManager::CTYPE_PDU_KEEPALIVE))
		m_timer.Schedule(this, (time_t)m_keepAliveInterval);

	if (lastStatus==CS_RECONNECTED)
	{
		RT_INFO_TRACE_THIS("CRtConnRlbTcpClient::OnRecvConnResp, reconnecting case, channel="<<respPdu.GetChannelId()<<" ack="<<respPdu.GetAck()<<" ticket="<<m_ticket);

		m_unackedDataLen = 0;
		m_isDataReceived = FALSE;
		m_isDataSent = FALSE;
		m_continuousIdleTime = 0;

		m_sendBuffer.OnReconnect(m_ackFromPeer);
		if (m_needOnSend && m_sendBuffer.HaveSpace())
		{
			m_needOnSend = FALSE;
			if (m_sink)
				m_sink->OnSend(this);
		}
		return;
	}

	RT_INFO_TRACE_THIS("CRtConnRlbTcpClient::OnRecvConnResp, channel="<<respPdu.GetChannelId()<<" connType="<<RT_CONN_TYPE_STRING(m_connType)<<" ticket="<<m_ticket);
	RT_ASSERTE(lastStatus==CS_CONNECTED);
	m_connConnector->GetConnectorSink()->OnConnectIndication(RT_OK, this, m_connConnector.ParaIn());
}

void CRtConnRlbTcpClient::OnRecvDisconn()
{
	RT_INFO_TRACE_THIS("CRtConnRlbTcpClient::OnRecvDisconn, trpt="<<m_lowerTransport.Get()<<" status="<<m_status);
	RT_ASSERTE(m_status!=CS_CONNECTED);

	Close_i();
	if (m_sink)
		m_sink->OnDisconnect(RT_ERROR_NETWORK_SOCKET_CLOSE, this);
	m_connConnector = NULL;
}

void CRtConnRlbTcpClient::OnRecvRtt(DWORD inTimestamp)
{
	DWORD rtt = ((DWORD)GetTickCountEx()) - inTimestamp;
	if ( rtt>=50 &&
		( rtt>1000 || (rtt>m_lastRtt && rtt-m_lastRtt>m_lastRtt/5) || (rtt<m_lastRtt && m_lastRtt-rtt>m_lastRtt/5) ) )
	{
		RT_INFO_TRACE_THIS("CRtConnRlbTcpClient::OnRecvRtt, rtt="<<rtt<<" BuffSize="<<m_sendBuffer.m_curSize);
		m_lastRtt = rtt;
	}
}

void CRtConnRlbTcpClient::OnException(RtResult inReason)
{
	if (m_status==CS_UNCONNECTED)
		return;

	RT_INFO_TRACE_THIS("CRtConnRlbTcpClient::OnException, reason="<<inReason<<" status="<<m_status);

	m_reconnectTimer.Cancel();
	SendDisconn();

	WORD lastStatus = m_status;
	Close_i(inReason);
	if (lastStatus==CS_CONNECTED)
		m_connConnector->GetConnectorSink()->OnConnectIndication(inReason, NULL, m_connConnector.ParaIn());
	else if (m_sink)
		m_sink->OnDisconnect(inReason, this);
	m_connConnector = NULL;
}

RtResult CRtConnRlbTcpClient::SendConnReq()
{
	WORD connAttr = (WORD)(m_connType>>CT_PROTO_LEN);
	CRtConnRlbTcpPduConnReq reqPdu(connAttr, m_channelId, m_ackToPeer, m_ticket);
	CRtMessageBlock mb(CRtConnRlbTcpPduBase::GetFixLength(RT_CONN_RLBTCP_PDU_CONN_REQ));
	reqPdu.Encode(mb);
	
	RtResult rv = m_lowerTransport->SendData(mb);
	RT_ASSERTE(RT_SUCCEEDED(rv));
	return rv;
}

//////////////////////////////////////////////////////////////////////////
// class CRtConnRlbTcpServer
//////////////////////////////////////////////////////////////////////////

CRtConnRlbTcpServer::CRtConnRlbTcpServer(CRtConnectionManager::CType inConnType)
		: CRtConnRlbTcp(inConnType)
{
	m_timer.Schedule(this, (time_t)RT_CONN_RECONNECT_TIMEOUT, 1);
}

CRtConnRlbTcpServer::~CRtConnRlbTcpServer()
{
}

RtResult CRtConnRlbTcpServer::Disconnect(RtResult inReason)
{
	if (m_status==CS_UNCONNECTED)
		return RT_OK;

	RT_INFO_TRACE_THIS("CRtConnRlbTcpServer::Disconnect, reason="<<inReason<<" status="<<m_status);

	CRtAutoPtr<CRtConnRlbTcpServer> svrThis(this);
	m_sink = NULL;
	SendDisconn();

	if (m_channelId!=0)
		m_connAcceptor->GetServerList()->RemoveServer(m_channelId);

	Close_i(inReason);
	return RT_OK;
}

void CRtConnRlbTcpServer::OnDisconnect(RtResult inReason, IRtTransport* inTransport)
{
	RT_INFO_TRACE_THIS("CRtConnRlbTcpServer::OnDisconnect, reason="<<inReason<<" trpt="<<inTransport<<" status="<<m_status);

	m_lowerTransport = NULL;
	ReleaseRecvBuffer();
	m_sendBuffer.ReleaseEncodedData();

	CRtAutoPtr<CRtConnRlbTcpServer> svrThis(this);

	if (m_status==CS_CONNECTED)
	{
		SetStatus(CS_UNCONNECTED);
		return;
	}

	if (m_status==CS_HANDSHAKE_OK)
	{
		m_timer.Cancel();
		m_sendDelayTimer.Cancel();
		if ( RT_BIT_DISABLED(m_connType, CRtConnectionManager::CTYPE_PDU_RECONNECT) )
		{
			SetStatus(CS_UNCONNECTED);
			RT_ASSERTE(m_sink);
			if (m_sink)
				m_sink->OnDisconnect(inReason, this);
			return;
		}

		SetStatus(CS_WAIT_RECONNECT);
		m_timer.Schedule(this, (time_t)RT_CONN_RECONNECT_TIMEOUT, 1);
		return;
	}

	RT_ASSERTE(m_status==CS_UNCONNECTED);
}

void CRtConnRlbTcpServer::OnConnectIndication(
		RtResult inResult,
		IRtTransport* inTransport,
		IRtAcceptorConnectorId* inRequestId)
{
	RT_INFO_TRACE_THIS("CRtConnRlbTcpServer::OnConnectIndication, trpt="<<inTransport);

	SetStatus(CS_CONNECTED);
	m_lowerTransport = inTransport;
	m_lowerTransport->OpenWithSink(this);
}

void CRtConnRlbTcpServer::OnTimer(CRtTimerWrapper* inId)
{
	if (&m_sendDelayTimer==inId)
	{
		if (m_sendBuffer.m_encodedData!=NULL)
			return;

		RT_ASSERTE_RETURN_VOID(m_status==CS_HANDSHAKE_OK);
		m_sendBuffer.SendBuffer();
		return;
	}

	RT_ASSERTE(&m_timer==inId);

	if (m_status!=CS_HANDSHAKE_OK)
	{
		RT_WARNING_TRACE_THIS("CRtConnRlbTcpServer::OnTimer, wait reconnecting timeout! status="<<m_status);
		goto close_end;
	}

	if (!m_isDataReceived)
	{
		m_continuousIdleTime += m_keepAliveInterval;
		if (m_continuousIdleTime>=RT_CONN_IDLE_TIMEOUT)
		{
			RT_WARNING_TRACE_THIS("CRtConnRlbTcpServer::OnTimer, connection idle timeout! time="<<m_continuousIdleTime);
			goto close_end;
		}
	}
	else
	{
		m_continuousIdleTime = 0;
		m_isDataReceived = FALSE;
	}

	if (!m_isDataSent)
		SendKeepAlive();
	else
		m_isDataSent = FALSE;

	return;

close_end:

	CRtAutoPtr<CRtConnRlbTcpServer> svrThis(this);

	if (m_channelId!=0)
		m_connAcceptor->GetServerList()->RemoveServer(m_channelId);

	WORD lastStatus = m_status;
	Close_i(RT_ERROR_NETWORK_CONNECT_TIMEOUT);

	if (lastStatus!=CS_CONNECTED && m_sink)
		m_sink->OnDisconnect(RT_ERROR_NETWORK_CONNECT_TIMEOUT, this);
}

void CRtConnRlbTcpServer::OnRecvConnReq()
{
	RT_ASSERTE(m_status==CS_CONNECTED);
	/// Cancel connect-timeout timer. 
	m_timer.Cancel();
	CRtAutoPtr<CRtConnRlbTcpServer> svrThis(this);
	RtResult failResult = RT_ERROR_FAILURE;

	if ( !RT_BIT_CMP_MASK(m_connAcceptor->GetConnType(), m_connType, m_connType) )
	{
		RT_WARNING_TRACE_THIS("CRtConnRlbTcpServer::OnRecvConnReq, invalid connType="<<m_connType);
		failResult = RT_ERROR_NETWORK_INVALID_HANDSHAKE;
		goto fail;
	}

	if (m_channelId!=0)
	{
		/// Reconnecting case.

		RT_ASSERTE(RT_BIT_ENABLED(m_connType, CRtConnectionManager::CTYPE_PDU_RECONNECT) );

		CRtAutoPtr<CRtConnRlbTcpServer> oldServer = m_connAcceptor->GetServerList()->GetServer(m_channelId);
		if (oldServer==NULL)
		{
			RT_WARNING_TRACE_THIS("CRtConnRlbTcpServer::OnRecvConnReq, reconnect server not found! channel="<<m_channelId);
			failResult = RT_ERROR_NETWORK_SOCKET_RESET;
			goto fail;
		}

		if (oldServer->m_ticket!=m_ticket)
		{
			RT_WARNING_TRACE_THIS("CRtConnRlbTcpServer::OnRecvConnReq, ticket not match! newticket="<<m_ticket<<" oldticket="<<oldServer->m_ticket<<" channel="<<m_channelId<<" oldserver="<<oldServer.Get());
			failResult = RT_ERROR_NETWORK_TICKET_NOT_MATCH;
			goto fail;
		}

		RT_INFO_TRACE_THIS("CRtConnRlbTcpServer::OnRecvConnReq, reconnect case. server="<<oldServer.Get()<<" channel="<<m_channelId<<" ack="<<m_ackFromPeer<<" ticket="<<m_ticket);

		RT_ASSERTE(oldServer->m_status!=CS_UNCONNECTED);
		CRtConnRlbTcpServer::Retrieve(this, oldServer.ParaIn());
		return;
	}

	/// New connection coming.

	if ( RT_BIT_ENABLED(m_connType, CRtConnectionManager::CTYPE_PDU_RECONNECT) )
	{
		m_channelId = m_connAcceptor->GetServerList()->AddServer(this);
		if (m_channelId==0)
		{
			/// Connection channel not available. 
			RT_WARNING_TRACE_THIS("CRtConnRlbTcpServer::OnRecvConnReq, too many connections!");
			failResult = RT_ERROR_NETWORK_CHANNEL_NOT_AVAILABLE;
			goto fail;
		}
	}

	m_ticket = GenerateTicket();

	RT_INFO_TRACE_THIS("CRtConnRlbTcpServer::OnRecvConnReq, channel="<<m_channelId<<" connType="<<RT_CONN_TYPE_STRING(m_connType)<<" ticket="<<m_ticket);

	SendConnResp(RT_OK);
	SetStatus(CS_HANDSHAKE_OK);
	if (RT_BIT_ENABLED(m_connType, CRtConnectionManager::CTYPE_PDU_KEEPALIVE))
		m_timer.Schedule(this, (time_t)m_keepAliveInterval);

	m_connAcceptor->GetAcceptorSink()->OnConnectIndication(RT_OK, this, m_connAcceptor.ParaIn());
	return;

fail:
	SendConnResp(failResult);
	Close_i();
}

void CRtConnRlbTcpServer::OnRecvDisconn()
{
	RT_INFO_TRACE_THIS("CRtConnRlbTcpServer::OnRecvDisconn, trpt="<<m_lowerTransport.Get()<<" status="<<m_status);
	RT_ASSERTE(m_status!=CS_CONNECTED);

//	CRtAutoPtr<CRtConnRlbTcpServer> svrThis(this);

	if (m_channelId!=0)
		m_connAcceptor->GetServerList()->RemoveServer(m_channelId);

	Close_i();
	RT_ASSERTE(m_sink);
	if (m_sink)
		m_sink->OnDisconnect(RT_ERROR_NETWORK_SOCKET_CLOSE, this);
}

void CRtConnRlbTcpServer::OnRecvRtt(DWORD inTimestamp)
{
	SendRtt(inTimestamp);
}

void CRtConnRlbTcpServer::OnException(RtResult inReason)
{
	if (m_status==CS_UNCONNECTED)
		return;

	RT_INFO_TRACE_THIS("CRtConnRlbTcpServer::OnException, reason="<<inReason<<" status="<<m_status);

	CRtAutoPtr<CRtConnRlbTcpServer> svrThis(this);
	SendDisconn();

	if (m_channelId!=0)
		m_connAcceptor->GetServerList()->RemoveServer(m_channelId);

	Close_i(inReason);
	if (m_sink)
		m_sink->OnDisconnect(inReason, this);
}

RtResult CRtConnRlbTcpServer::SendConnResp(RtResult inResult)
{
	CRtConnRlbTcpPduConnResp respPdu(inResult, m_channelId, m_ackToPeer, m_ticket);
	CRtMessageBlock mb(CRtConnRlbTcpPduBase::GetFixLength(RT_CONN_RLBTCP_PDU_CONN_RESP));
	respPdu.Encode(mb);
	
	RtResult rv = m_lowerTransport->SendData(mb);
	RT_ASSERTE(RT_SUCCEEDED(rv));
	return rv;
}

DWORD CRtConnRlbTcpServer::GenerateTicket()
{
	if (m_lowerTransport==NULL)
		return 0;

	CRtInetAddr peerAddr;
	m_lowerTransport->GetOption(RT_OPT_TRANSPORT_PEER_ADDR, &peerAddr);
	return peerAddr.GetPtr()->sin_addr.s_addr + peerAddr.GetPtr()->sin_port;
}

RtResult CRtConnRlbTcpServer::Retrieve(CRtConnRlbTcpServer* inNewServer, CRtConnRlbTcpServer* inOldServer)
{
	if (inOldServer->m_status==CS_HANDSHAKE_OK)
	{
		RT_WARNING_TRACE("CRtConnRlbTcpServer::Retrieve, old server status is OK! trpt="<<inOldServer->m_lowerTransport.Get()<<" old="<<inOldServer<<" new="<<inNewServer);
		inOldServer->ReleaseRecvBuffer();
// 		inOldServer->SendDisconn();
		inOldServer->m_sendBuffer.ReleaseEncodedData();
		inOldServer->m_lowerTransport->Disconnect(RT_OK);
	}
	inOldServer->m_lowerTransport = inNewServer->m_lowerTransport;
	inNewServer->m_lowerTransport = NULL;
	inOldServer->m_lowerTransport->OpenWithSink(inOldServer);
	inOldServer->SendConnResp(RT_OK);
	inOldServer->SetStatus(CS_HANDSHAKE_OK);

	inOldServer->Reset4Recv();
	inOldServer->m_unackedDataLen = 0;

	inOldServer->m_timer.Cancel();
	inOldServer->m_sendDelayTimer.Cancel();
	if ( RT_BIT_ENABLED(inOldServer->m_connType, CRtConnectionManager::CTYPE_PDU_KEEPALIVE) )
	{
		inOldServer->m_isDataReceived = FALSE;
		inOldServer->m_isDataSent = FALSE;
		inOldServer->m_continuousIdleTime = 0;
		inOldServer->m_timer.Schedule(inOldServer, (time_t)inOldServer->m_keepAliveInterval);
	}

	inOldServer->m_ackFromPeer = inNewServer->m_ackFromPeer;
	inOldServer->m_sendBuffer.OnReconnect(inOldServer->m_ackFromPeer);
	if (inOldServer->m_needOnSend && inOldServer->m_sendBuffer.HaveSpace())
	{
		inOldServer->m_needOnSend = FALSE;
		if (inOldServer->m_sink)
			inOldServer->m_sink->OnSend(inOldServer);
	}
	return RT_OK;
}

#endif // !RT_USE_OLD_CONN_WRAP
